const { KBinary, ObjCoder, StructValueType: SVT, StructCoder } = require("../dist/kcoder");

let b = new KBinary();

test("byte", () => {
    let arr = [0x0, 0x1, 0xff];
    for (let num of arr) {
        b.wStart().wByte(num);
        b.rStart(b.wEnd());
        expect(b.rByte()).toBe(num);
        b.rEnd();
    };
})

test("bytes", () => {
    let arr = [new Uint8Array(0), new Uint8Array(8), new Uint8Array(16), new Uint8Array(32), new Uint8Array(64)];
    for (let buf of arr) {
        for (let i = 0; i < buf.length; i++) {
            buf[i] = Math.floor(Math.random() * 256);
        }
        b.wStart().wBytes(buf);
        b.rStart(b.wEnd());
        expect(b.rBytes()).toEqual(buf);
        b.rEnd();
    };
});

test("uint8", () => {
    let arr = [0x0, 0x1, 0xff];
    for (let num of arr) {
        b.wStart().wUint8(num);
        b.rStart(b.wEnd());
        expect(b.rUint8()).toBe(num);
        b.rEnd();
    };
});

test("uint16", () => {
    let arr = [0x0, 0x1, 0xff, 0xffff];
    for (let num of arr) {
        b.wStart().wUint16(num);
        b.rStart(b.wEnd());
        expect(b.rUint16()).toBe(num);
        b.rEnd();
    }
});

test("uint32", () => {
    let arr = [0x0, 0x1, 0xff, 0xffff, 0xffffff, 0xffffffff];
    for (let num of arr) {
        b.wStart().wUint32(num);
        b.rStart(b.wEnd());
        expect(b.rUint32()).toBe(num);
        b.rEnd();
    }
});

test("uint64", () => {
    let arr = [0x0, 0x1, 0xff, 0xffff, 0xffffff, 0xffffffff, 0xffffffffff, 0xffffffffffff, 0x1fffffffffffff];
    for (let num of arr) {
        b.wStart().wUint64(num);
        b.rStart(b.wEnd());
        expect(b.rUint64()).toBe(num);
        b.rEnd();
    }
});

test("auint32", () => {
    let arr = [0x0, 0x1, 0xff, 0xffff, 0xffffff, 0xffffffff];
    for (let num of arr) {
        b.wStart().wAuint32(num);
        b.rStart(b.wEnd());
        expect(b.rAuint32()).toBe(num);
        b.rEnd();
    }
});

test("auint64", () => {
    let arr = [0x0, 0x1, 0xff, 0xffff, 0xffffff, 0xffffffff, 0xffffffffff, 0xffffffffffff, 0x1fffffffffffff];
    for (let num of arr) {
        b.wStart().wAuint64(num);
        b.rStart(b.wEnd());
        expect(b.rAuint64()).toBe(num);
        b.rEnd();
    }
});

test("int8", () => {
    let arr = [0x0, 0x1, -0x1, 0x7f, -0x80];
    for (let num of arr) {
        b.wStart().wInt8(num);
        b.rStart(b.wEnd());
        expect(b.rInt8()).toBe(num);
        b.rEnd();
    }
});

test("int16", () => {
    let arr = [0x0, 0x1, -0x1, 0x7f, -0x80, 0x7fff, -0x8000];
    for (let num of arr) {
        b.wStart().wInt16(num);
        b.rStart(b.wEnd());
        expect(b.rInt16()).toBe(num);
        b.rEnd();
    }
});

test("int32", () => {
    let arr = [0x0, 0x1, -0x1, 0x7f, -0x80, 0x7fff, -0x8000, 0x7fffff, -0x800000];
    for (let num of arr) {
        b.wStart().wInt32(num);
        b.rStart(b.wEnd());
        expect(b.rInt32()).toBe(num);
        b.rEnd();
    }
});

test("int64", () => {
    let arr = [0x0, 0x1, -0x1, 0x7f, -0x80, 0x7fff, -0x8000, 0x7fffff, -0x800000, 0x7fffffff, -0x80000000,
        0x7fffffffff, -0x8000000000, 0x7fffffffffff, -0x800000000000, 0x1fffffffffffff, -0x1fffffffffffff];
    for (let num of arr) {
        b.wStart().wInt64(num);
        b.rStart(b.wEnd());
        expect(b.rInt64()).toBe(num);
        b.rEnd();
    }
});

test("aint32", () => {
    let arr = [0x0, 0x1, -0x1, 0x7f, -0x80, 0x7fff, -0x8000, 0x7fffff, -0x800000];
    for (let num of arr) {
        b.wStart().wAint32(num);
        b.rStart(b.wEnd());
        expect(b.rAint32()).toBe(num);
        b.rEnd();
    }
});

test("aint64", () => {
    let arr = [0x0, 0x1, -0x1, 0x7f, -0x80, 0x7fff, -0x8000, 0x7fffff, -0x800000, 0x7fffffff, -0x80000000,
        0x7fffffffff, -0x8000000000, 0x7fffffffffff, -0x800000000000, 0x1fffffffffffff, -0x1fffffffffffff];
    for (let num of arr) {
        b.wStart().wAint64(num);
        b.rStart(b.wEnd());
        expect(b.rAint64()).toBe(num);
        b.rEnd();
    }
});

test("float32", () => {
    let arr = [0, 1, 0.1, -0.1, 1.11, -1.11, 6666.6666, -6666.6666];
    for (let num of arr) {
        b.wStart().wFloat32(num);
        b.rStart(b.wEnd());
        expect(Math.abs(b.rFloat32() - num)).toBeLessThanOrEqual(1E-4);
        b.rEnd();
    }
});

test("str", () => {
    let arr = ["", "0123456789", "abcdefghijklmn", "!@#$%^&*()_+", "IIIIIIIVVVIVIIVIIIIX", "中文", "犇猋骉麤", "😂😘😍😄😊😋😍"];
    for (let str of arr) {
        b.wStart().wStr(str);
        b.rStart(b.wEnd());
        expect(b.rStr()).toBe(str);
        b.rEnd();
    }
});

let OBJ = { a: 0, b: 1, c: -1, d: 1.1, e: -1.1, f: "", g: "123", h: new Uint8Array(8), i: [], j: {}, k: true, l: false };
let ARR = [0x0, 0x1, -0x1, 0x7f, -0x80, 0x7fff, -0x8000, 0x7fffff, -0x800000, 0x7fffffff, -0x80000000,
    0x7fffffffff, -0x8000000000, 0x7fffffffffff, -0x800000000000, 0x1fffffffffffff, -0x1fffffffffffff,
    0.1, -0.1, 1.11, -1.11, 6666.6666, -6666.6666,
    "", "0123456789", "abcdefghijklmn", "!@#$%^&*()_+", "IIIIIIIVVVIVIIVIIIIX", "中文", "犇猋骉麤", "😂😘😍😄😊😋😍", new Uint8Array(8), undefined, null];

test("ObjCoder", () => {
    let arr = [...ARR, OBJ];
    let obj = { ...OBJ, arr };
    for (let val of arr) {
        expect(ObjCoder.decode(ObjCoder.encode(val).wEnd())).toEqual(val);
        b.wStart().wAuint32(1);
        ObjCoder.encode(val, b);
        b.wAuint32(2);
        b.rStart(b.wEnd());
        expect(b.rAuint32()).toBe(1);
        let obj = ObjCoder.decode(b);
        expect(obj).toEqual(val);
        expect(b.rAuint32()).toBe(2);
    }
    expect(ObjCoder.decodeArr(ObjCoder.encodeArr(arr).wEnd())).toEqual(arr);
    b.wStart().wAuint32(1);
    ObjCoder.encodeArr(arr, b);
    b.wAuint32(2);
    b.rStart(b.wEnd());
    expect(b.rAuint32()).toBe(1);
    expect(ObjCoder.decodeArr(b)).toEqual(arr);
    expect(b.rAuint32()).toBe(2);
    expect(ObjCoder.decodeObj(ObjCoder.encodeObj(obj).wEnd())).toEqual(obj);
    b.wStart();
    b.wAuint32(1);
    ObjCoder.encodeObj(obj, b);
    b.wAuint32(2);
    b.rStart(b.wEnd());
    expect(b.rAuint32()).toBe(1);
    expect(ObjCoder.decodeObj(b)).toEqual(obj);
    expect(b.rAuint32()).toBe(2);
});

test("StructCoder", () => {
    let child = {
        byte: 0xff, bytes: new Uint8Array(8), boolT: true, boolF: false,
        uint8: 0xff, uint16: 0xffff, uint32: 0xffffffff, uint64: 0x1fffffffffffff, auint32: 0xffffffff, auint64: 0x1fffffffffffff,
        int8: -0x80, int16: -0x8000, int32: -0x80000000, int64: -0x1fffffffffffff, aint32: -0x80000000, aint64: -0x1fffffffffffff,
        uint64Zero: 0x0, auint64Zero: 0x0, int64Zero: 0x0, aint64Zero: 0x0,
        floatAuint32: 1.1111, floatAuint64: 122222222222.1111, floatAint32: -1.1111, floatAint64: -122222222222.1111, floatStr: -1234.56789,
        str: "0123456789abcdefghijklmn!@#$%^&*()_+IIIIIIIVVVIVIIVIIIIX中文犇猋骉麤😂😘😍😄😊😋😍",
        obj: OBJ, arr: ARR
    };

    let childStruct = [
        ["byte?", SVT.byte], ["bytes?", SVT.bytes], ["boolT", SVT.bool], ["boolF", SVT.bool],
        ["uint8", SVT.uint8], ["uint16", SVT.uint16], ["uint32", SVT.uint32], ["uint64", SVT.uint64], ["auint32", SVT.auint32], ["auint64", SVT.auint64],
        ["int8", SVT.int8], ["int16", SVT.int16], ["int32", SVT.int32], ["int64", SVT.int64], ["aint32", SVT.aint32], ["aint64", SVT.aint64],
        ["uint64Zero", SVT.uint64], ["auint64Zero", SVT.auint64], ["int64Zero", SVT.int64], ["aint64Zero", SVT.aint64],
        ["floatAuint32", SVT.floatAuint32, 0, 4], ["floatAuint64", SVT.floatAuint64, 0, 4], ["floatAint32", SVT.floatAint32, 0, 4], ["floatAint64", SVT.floatAint64, 0, 4],
        ["floatStr", SVT.floatStr], ["str", SVT.str],
        ["obj", SVT.obj], ["arr", SVT.arr]
    ];

    let obj = {
        struct: child,
        byteArr: [0xff], bytesArr: [new Uint8Array(8)], boolArr: [true, false],
        uint8Arr: [0xff], uint16Arr: [0xffff], uint32Arr: [0xffffffff], uint64Arr: [0x1fffffffffffff], auint32Arr: [0xffffffff], auint64Arr: [0x1fffffffffffff],
        int8Arr: [-0x80], int16Arr: [-0x8000], int32Arr: [-0x80000000], int64Arr: [-0x1fffffffffffff], aint32Arr: [-0x80000000], aint64Arr: [-0x1fffffffffffff],
        floatAuint32Arr: [1.1111], floatAuint64Arr: [122222222222.1111], floatAint32Arr: [-1.1111], floatAint64Arr: [-122222222222.1111], floatStrArr: [-1234.56789],
        strArr: ["0123456789abcdefghijklmn!@#$%^&*()_+IIIIIIIVVVIVIIVIIIIX中文犇猋骉麤😂😘😍😄😊😋😍"],
        objArr: [OBJ], arrArr: [ARR],
        structArr: [child],
        structArr2: [[child], [child]],
        structArr3: [[[child], [child]], [[child], [child]]]
    }

    let struct = [
        ["struct", SVT.struct, 0, childStruct],
        ["byteArr?", SVT.byte, 1], ["bytesArr?", SVT.bytes, 1], ["boolArr", SVT.bool, 1],
        ["uint8Arr", SVT.uint8, 1], ["uint16Arr", SVT.uint16, 1], ["uint32Arr", SVT.uint32, 1], ["uint64Arr", SVT.uint64, 1], ["auint32Arr", SVT.auint32, 1], ["auint64Arr", SVT.auint64, 1],
        ["int8Arr", SVT.int8, 1], ["int16Arr", SVT.int16, 1], ["int32Arr", SVT.int32, 1], ["int64Arr", SVT.int64, 1], ["aint32Arr", SVT.aint32, 1], ["aint64Arr", SVT.aint64, 1],
        ["floatAuint32Arr", SVT.floatAuint32, 1, 4], ["floatAuint64Arr", SVT.floatAuint64, 1, 4], ["floatAint32Arr", SVT.floatAint32, 1, 4], ["floatAint64Arr", SVT.floatAint64, 1, 4],
        ["floatStrArr", SVT.floatStr, 1], ["strArr", SVT.str, 1],
        ["objArr", SVT.obj, 1], ["arrArr", SVT.arr, 1],
        ["structArr", SVT.struct, 1, childStruct],
        ["structArr2", SVT.struct, 2, childStruct],
        ["structArr3", SVT.struct, 3, childStruct],
    ]

    let coder = new StructCoder(struct);
    expect(coder.decode(coder.encode(obj).wEnd())).toEqual(obj);

    coder.encode(obj, b.wStart());
    coder.encode(obj, b);
    b.rStart(b.wEnd());
    expect(coder.decode(b)).toEqual(obj);
    expect(coder.decode(b)).toEqual(obj);

    b.wStart().wAuint32(1);
    coder.encode(obj, b);
    b.wAuint32(2);
    b.rStart(b.wEnd());
    expect(b.rAuint32()).toBe(1);
    expect(coder.decode(b)).toEqual(obj);
    expect(b.rAuint32()).toBe(2);

    let tmp = [{ a: 1 }, { a: 1, b: 1 }, { a: 1, b: 1, c: 1 }, { a: 1, c: 1 }];
    coder = new StructCoder([["a", SVT.auint32], ["b?", SVT.auint32], ["c?", SVT.auint32]]);
    b.wStart();
    for (let t of tmp) {
        coder.encode(t, b);
    }
    b.rStart(b.wEnd());
    for (let t of tmp) {
        expect(coder.decode(b)).toEqual(t);
    }
})